"""
订阅管理相关的视图模块
包含数据源管理、OPC UA订阅和MQTT订阅的所有视图类和函数
"""

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib import messages
from django.http import JsonResponse
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from django.contrib.auth.decorators import login_required
from django.forms import inlineformset_factory
from .models import DataSource, OPCUASubscription, MQTTSubscription, MqttSubscriptionItem
from .serializers import (
    DataSourceSerializer, 
    OPCUASubscriptionSerializer, 
    MQTTSubscriptionSerializer
)
from .services import DataSourceService
import logging
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
import json

logger = logging.getLogger(__name__)

# ================ 数据源管理视图 ================
@login_required
def test_datasource_connection(request, pk):
    """测试数据源连接
    
    Args:
        request: HTTP请求对象
        pk: 数据源ID
    
    Returns:
        JsonResponse: 包含测试结果的JSON响应
        {
            'success': 测试是否成功,
            'message': 测试结果消息
        }
    """
    try:
        datasource = get_object_or_404(DataSource, pk=pk)
        success, message = DataSourceService.test_connection(
            datasource.type,
            datasource.connection_string
        )
        return JsonResponse({
            'success': success,
            'message': message
        })
    except Exception as e:
        logger.error(f"测试数据源连接失败 - ID: {pk}, 错误: {str(e)}", exc_info=True)
        return JsonResponse({
            'success': False,
            'message': f'测试失败: {str(e)}'
        })

@login_required
def toggle_opcua_subscription(request, pk):
    """切换OPC UA订阅的启用/禁用状态
    
    Args:
        request: HTTP请求对象
        pk: 订阅ID
    
    Returns:
        JsonResponse: 包含操作结果的JSON响应
    """
    try:
        subscription = get_object_or_404(OPCUASubscription, pk=pk, created_by=request.user)
        subscription.is_active = not subscription.is_active
        subscription.save()
        
        status = '启用' if subscription.is_active else '禁用'
        logger.info(f"OPC UA订阅状态已切换 - ID: {subscription.id}, 状态: {status}")
        
        return JsonResponse({
            'success': True,
            'status': status
        })
    except Exception as e:
        logger.error(f"切换OPC UA订阅状态失败 - ID: {pk}, 错误: {str(e)}", exc_info=True)
        return JsonResponse({
            'success': False,
            'message': str(e)
        })

@login_required
def toggle_mqtt_subscription(request, pk):
    """切换 MQTT 订阅的启用状态"""
    try:
        subscription = get_object_or_404(MQTTSubscription, pk=pk, created_by=request.user)
        old_status = subscription.is_active
        subscription.is_active = not old_status
        
        # 使用update方法避免触发信号
        MQTTSubscription.objects.filter(pk=pk).update(is_active=not old_status)
        
        status = '启用' if not old_status else '禁用'
        logger.info(f"【MQTT订阅】状态已切换 - ID: {subscription.id}, 状态: {status}")
        
        # 手动触发订阅任务
        from .tasks import sync_mqtt_subscription
        if not old_status:  # 如果之前是禁用状态，现在要启用
            task = sync_mqtt_subscription.delay(
                subscription_id=subscription.id,
                topic=subscription.topic,
                broker_url=subscription.data_source.connection_string
            )
            logger.info(f"手动触发MQTT订阅任务 - ID: {subscription.id}, Task ID: {task.id}")
        else:  # 如果之前是启用状态，现在要禁用
            task = sync_mqtt_subscription.delay(
                subscription_id=subscription.id,
                action='cancel'
            )
            logger.info(f"手动触发MQTT订阅取消任务 - ID: {subscription.id}, Task ID: {task.id}")
        
        return JsonResponse({'success': True, 'status': status})
    except Exception as e:
        logger.error(f"切换MQTT订阅状态失败 - ID: {pk}, 错误: {str(e)}", exc_info=True)
        return JsonResponse({'success': False, 'message': str(e)})

@login_required
@require_http_methods(["POST"])
def toggle_mqtt_item(request, item_id):
    """切换MQTT监控项的启用状态"""
    try:
        # 获取监控项
        item = MqttSubscriptionItem.objects.select_related('subscription').get(
            id=item_id,
            subscription__created_by=request.user
        )
        
        # 解析请求数据
        data = json.loads(request.body)
        is_active = data.get('is_active', not item.is_active)
        print(item)
        # 更新状态
        item.is_active = is_active
        item.save()
        logger.info(f"【MQTT订阅 - 监控项】状态已切换 - ID: {item.subscription_id}-{item.id}, 状态: {'启用' if is_active else '禁用'}")

        # 如果订阅是启用状态，则需要更新WebSocket连接状态
        if item.subscription.is_active:
            # 获取channel layer
            from channels.layers import get_channel_layer
            from asgiref.sync import async_to_sync
            channel_layer = get_channel_layer()

            # 发送状态更新消息
            async_to_sync(channel_layer.group_send)(
                f"user_{request.user.id}_subscriptions",
                {
                    'type': 'subscription_data',
                    'data': {
                        'type': 'status_update',
                        'subscription_type': 'mqtt',
                        'subscription_id': item.subscription.id,
                        'item_id': item.id,
                        'status': 'connected' if is_active else 'disconnected'
                    }
                }
            )
            
            logger.info(
                f"MQTT监控项状态已更新 - ID: {item.id}, "
                f"订阅: {item.subscription.topic}, "
                f"监控项: {item.name}, "
                f"状态: {'启用' if is_active else '禁用'}"
            )
        
        # 返回成功响应
        return JsonResponse({
            'success': True,
            'status': '启用' if is_active else '禁用'
        })
        
    except MqttSubscriptionItem.DoesNotExist:
        return JsonResponse({
            'success': False,
            'message': '监控项不存在'
        }, status=404)
    except Exception as e:
        logger.error(f"切换MQTT监控项状态失败 - ID: {item_id}, 错误: {str(e)}", exc_info=True)
        return JsonResponse({
            'success': False,
            'message': str(e)
        }, status=500)

# API视图集
class DataSourceViewSet(viewsets.ModelViewSet):
    """数据源API视图集"""
    queryset = DataSource.objects.all()
    serializer_class = DataSourceSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['type']

    def get_permissions(self):
        """根据不同的操作返回不同的权限"""
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            # 创建、更新和删除操作需要相应的权限
            permission_classes = [IsAuthenticated, DjangoModelPermissions]
        else:
            # 列表和详情查看只需要登录
            permission_classes = [IsAuthenticated]
        return [permission() for permission in permission_classes]

class OPCUASubscriptionViewSet(viewsets.ModelViewSet):
    """OPC UA订阅API视图集"""
    queryset = OPCUASubscription.objects.all()
    serializer_class = OPCUASubscriptionSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['data_source', 'is_active']

class MQTTSubscriptionViewSet(viewsets.ModelViewSet):
    """MQTT订阅API视图集"""
    queryset = MQTTSubscription.objects.all()
    serializer_class = MQTTSubscriptionSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['data_source', 'is_active']

# 仪表板视图
@login_required
def subscription_dashboard(request):
    """显示用户的所有订阅数据
    
    包含：
    1. OPC UA订阅列表及其实时数据
    2. MQTT订阅列表（包含主题和监控项）及其实时数据
    """
    # 查询OPC UA订阅
    opcua_subscriptions = OPCUASubscription.objects.filter(
        created_by=request.user,
        is_active=True
    ).select_related('data_source')
    
    # 查询MQTT订阅及其监控项
    mqtt_subscriptions = MQTTSubscription.objects.filter(
        created_by=request.user,
        is_active=True
    ).select_related('data_source').prefetch_related(
        'mqttsubscriptionitem_set'  # 预加载监控项
    )

    context = {
        'opcua_subscriptions': opcua_subscriptions,
        'mqtt_subscriptions': mqtt_subscriptions
    }
    return render(request, 'subscriptions/dashboard.html', context)

# ================ 数据源管理视图 ================
class DataSourceListView(LoginRequiredMixin, ListView):
    """数据源列表视图"""
    model = DataSource
    template_name = 'subscriptions/datasource_list.html'
    context_object_name = 'datasources'
    ordering = ['-created_at']

    def get_queryset(self):
        """返回所有数据源，因为所有用户都可以查看"""
        return DataSource.objects.all()

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 添加用户是否有管理权限的标志
        context['can_manage_datasource'] = self.request.user.has_perm('subscriptions.add_datasource')
        return context

class DataSourceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
    """数据源创建视图"""
    model = DataSource
    template_name = 'subscriptions/datasource_form.html'
    fields = ['name', 'type', 'connection_string', 'description']
    success_url = reverse_lazy('datasource_list')
    permission_required = 'subscriptions.add_datasource'

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        try:
            return super().form_valid(form)
        except Exception as e:
            messages.error(self.request, f'创建失败: {str(e)}')
            return super().form_invalid(form)

class DataSourceUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
    """数据源更新视图"""
    model = DataSource
    template_name = 'subscriptions/datasource_form.html'
    fields = ['name', 'type', 'connection_string', 'description']
    success_url = reverse_lazy('datasource_list')
    permission_required = 'subscriptions.change_datasource'

    def get_queryset(self):
        return super().get_queryset()

class DataSourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
    """数据源删除视图"""
    model = DataSource
    template_name = 'subscriptions/datasource_confirm_delete.html'
    success_url = reverse_lazy('datasource_list')
    permission_required = 'subscriptions.delete_datasource'

    def get_queryset(self):
        return super().get_queryset()

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['has_subscriptions'] = (
            self.object.opcuasubscription_set.exists() or 
            self.object.mqttsubscription_set.exists()
        )
        return context

# ================ OPC UA订阅视图 ================
class OPCUASubscriptionListView(LoginRequiredMixin, ListView):
    """OPC UA订阅列表视图"""
    model = OPCUASubscription
    template_name = 'subscriptions/opcua_subscription_list.html'
    context_object_name = 'subscriptions'
    ordering = ['-created_at']

    def get_queryset(self):
        return super().get_queryset().filter(created_by=self.request.user)

class OPCUASubscriptionCreateView(LoginRequiredMixin, CreateView):
    """OPC UA订阅创建视图"""
    model = OPCUASubscription
    template_name = 'subscriptions/opcua_subscription_form.html'
    fields = ['name', 'data_source', 'node_id', 'condition', 'threshold_value', 
              'threshold_max', 'alert_message', 'unit', 'is_active']
    success_url = reverse_lazy('opcua_subscription_list')

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        # 只显示OPC UA类型的数据源
        form.fields['data_source'].queryset = DataSource.objects.filter(type='opcua')
        return form

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        try:
            return super().form_valid(form)
        except Exception as e:
            messages.error(self.request, f'创建失败: {str(e)}')
            return super().form_invalid(form)

class OPCUASubscriptionUpdateView(LoginRequiredMixin, UpdateView):
    """OPC UA订阅更新视图"""
    model = OPCUASubscription
    template_name = 'subscriptions/opcua_subscription_form.html'
    fields = ['name', 'data_source', 'node_id', 'condition', 'threshold_value', 
              'threshold_max', 'alert_message', 'unit', 'is_active']
    success_url = reverse_lazy('opcua_subscription_list')

    def get_queryset(self):
        return super().get_queryset().filter(created_by=self.request.user)

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        # 只显示OPC UA类型的数据源
        form.fields['data_source'].queryset = DataSource.objects.filter(type='opcua')
        return form

class OPCUASubscriptionDeleteView(LoginRequiredMixin, DeleteView):
    """OPC UA订阅删除视图"""
    model = OPCUASubscription
    template_name = 'subscriptions/opcua_subscription_confirm_delete.html'
    success_url = reverse_lazy('opcua_subscription_list')

    def get_queryset(self):
        return super().get_queryset().filter(created_by=self.request.user)

# ================ MQTT订阅视图 ================
class MQTTSubscriptionListView(LoginRequiredMixin, ListView):
    """MQTT订阅列表视图"""
    model = MQTTSubscription
    template_name = 'subscriptions/mqtt_subscription_list.html'
    context_object_name = 'subscriptions'
    ordering = ['-created_at']

    def get_queryset(self):
        return super().get_queryset().filter(created_by=self.request.user)

class MQTTSubscriptionCreateView(LoginRequiredMixin, CreateView):
    """MQTT订阅创建视图"""
    model = MQTTSubscription
    template_name = 'subscriptions/mqtt_subscription_form.html'
    fields = ['data_source', 'topic', 'is_active']
    success_url = reverse_lazy('mqtt_subscription_list')

    def get_form(self, form_class=None):
        """获取表单并设置数据源选项
        
        只显示MQTT类型的数据源
        """
        form = super().get_form(form_class)
        form.fields['data_source'].queryset = DataSource.objects.filter(type='mqtt')
        return form

    def get_context_data(self, **kwargs):
        """获取上下文数据
        
        添加监控项表单集到上下文中
        """
        context = super().get_context_data(**kwargs)
        if self.request.POST:
            context['formset'] = MqttSubscriptionItemFormSet(
                self.request.POST,
                instance=self.object
            )
        else:
            context['formset'] = MqttSubscriptionItemFormSet(instance=self.object)
        return context

    def form_valid(self, form):
        """表单验证成功后的处理
        
        1. 设置创建者
        2. 保存主表单
        3. 保存监控项表单集
        4. 为每个监控项设置创建者
        """
        form.instance.created_by = self.request.user
        context = self.get_context_data()
        formset = context['formset']
        
        if formset.is_valid():
            try:
                self.object = form.save()
                formset.instance = self.object
                instances = formset.save(commit=False)
                for instance in instances:
                    instance.created_by = self.request.user
                    instance.save()
                formset.save_m2m()  # 保存多对多关系
                return redirect(self.success_url)
            except Exception as e:
                messages.error(self.request, f'创建失败: {str(e)}')
                return self.form_invalid(form)
        else:
            return self.form_invalid(form)

class MQTTSubscriptionUpdateView(LoginRequiredMixin, UpdateView):
    """MQTT订阅更新视图"""
    model = MQTTSubscription
    template_name = 'subscriptions/mqtt_subscription_form.html'
    fields = ['data_source', 'topic', 'is_active']
    success_url = reverse_lazy('mqtt_subscription_list')

    def get_queryset(self):
        """获取查询集
        
        只允许用户编辑自己创建的订阅
        """
        return super().get_queryset().filter(created_by=self.request.user)

    def get_form(self, form_class=None):
        """获取表单并设置数据源选项
        
        只显示MQTT类型的数据源
        """
        form = super().get_form(form_class)
        form.fields['data_source'].queryset = DataSource.objects.filter(type='mqtt')
        return form

    def get_context_data(self, **kwargs):
        """获取上下文数据
        
        添加监控项表单集到上下文中
        """
        context = super().get_context_data(**kwargs)
        if self.request.POST:
            context['formset'] = MqttSubscriptionItemFormSet(
                self.request.POST,
                instance=self.object
            )
        else:
            context['formset'] = MqttSubscriptionItemFormSet(instance=self.object)
        return context

    def form_valid(self, form):
        """表单验证成功后的处理
        
        1. 保存主表单
        2. 保存监控项表单集
        3. 为新增的监控项设置创建者
        4. 处理已删除的监控项
        """
        context = self.get_context_data()
        formset = context['formset']
        
        if formset.is_valid():
            try:
                self.object = form.save()
                formset.instance = self.object
                instances = formset.save(commit=False)
                for instance in instances:
                    instance.created_by = self.request.user
                    instance.save()
                # 处理已删除的实例
                for obj in formset.deleted_objects:
                    obj.delete()
                formset.save_m2m()  # 保存多对多关系
                return redirect(self.success_url)
            except Exception as e:
                messages.error(self.request, f'更新失败: {str(e)}')
                return self.form_invalid(form)
        else:
            return self.form_invalid(form)

class MQTTSubscriptionDeleteView(LoginRequiredMixin, DeleteView):
    """MQTT订阅删除视图
    
    处理MQTT订阅的删除操作
    - 只允许用户删除自己创建的订阅
    - 删除订阅时会级联删除所有相关的监控项
    """
    model = MQTTSubscription
    template_name = 'subscriptions/mqtt_subscription_confirm_delete.html'
    success_url = reverse_lazy('mqtt_subscription_list')

    def get_queryset(self):
        """获取查询集
        
        只允许用户删除自己创建的订阅
        """
        return super().get_queryset().filter(created_by=self.request.user)

# 创建内联表单集
MqttSubscriptionItemFormSet = inlineformset_factory(
    MQTTSubscription,  # 父模型
    MqttSubscriptionItem,  # 子模型
    fields=[  # 表单字段
        'name',  # 监控项名称
        'values_id',  # 数据键名
        'condition',  # 报警条件
        'threshold_value',  # 阈值
        'threshold_max',  # 最大阈值（范围条件使用）
        'alert_message',  # 报警消息
        'unit'  # 单位
    ],
    extra=1,  # 额外显示的空表单数量
    can_delete=True,  # 允许删除
    min_num=1,  # 最少需要的表单数量
    validate_min=True  # 验证最小表单数量
)
